/**
 * Project:   dtu.fmes.googleloon.ui
 * File:      MainWindowListener.java
 * License: 
 *            This file is licensed under GNU General Public License version 3
 *            http://www.gnu.org/licenses/gpl-3.0.txt
 *
 * Copyright: Bartosz Cichecki [ cichecki.bartosz@gmail.com ]
 * Date:      Nov 20, 2013
 */

package dtu.fmes.loon.ui.listener;

import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.swing.JFileChooser;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileNameExtensionFilter;

import org.apache.log4j.Logger;

import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;

import dtu.fmes.loon.model.Balloon;
import dtu.fmes.loon.model.SimulationStateHistory;
import dtu.fmes.loon.ui.MainWindow;
import dtu.fmes.loon.ui.draw.DrawController;
import dtu.fmes.loon.ui.utils.UiUtils;
import dtu.fmes.loon.utils.GsonUtil;

/**
 * @author Bartosz Cichecki
 * 
 */
public class MainWindowListener implements ActionListener, ChangeListener, MouseWheelListener, MouseMotionListener {

	private static final Logger log = Logger.getLogger(MainWindowListener.class);

	private static final int SMALL_STEP = 1;

	private static final int BIG_STEP = 10;

	private static final int TIMER_STEP = 1;

	private static final int TIMER_SPEED_MS = 20;

	private MainWindow mainWindow;

	private DrawController drawController;

	private Timer playTimer = new Timer(TIMER_SPEED_MS, this);

	public MainWindowListener(MainWindow mainWindow) {
		this.mainWindow = mainWindow;
	}

	/*
	 * (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == mainWindow.getMntmOpenSimulationResult()) {
			boolean opened = openSimulationResult();
			if (opened) {
				drawController.jumpToFrame(0);
				updateControls();
			}
		}
		if (e.getSource() == mainWindow.getMntmExit()) {
			exit();
		}
		if (e.getSource() == mainWindow.getMntmNextFrame() || e.getSource() == mainWindow.getBtnNext()) {
			drawController.rewindFrames(SMALL_STEP);
			updateControls();
		}
		if (e.getSource() == mainWindow.getMntmPreviousFrame() || e.getSource() == mainWindow.getBtnPrevious()) {
			drawController.rewindFrames(-SMALL_STEP);
			updateControls();
		}
		if (e.getSource() == mainWindow.getMntmBigStepNextFrame() || e.getSource() == mainWindow.getBtnBigStepNextFrame()) {
			drawController.rewindFrames(BIG_STEP);
			updateControls();
		}
		if (e.getSource() == mainWindow.getMntmBigStepPreviousFrame() || e.getSource() == mainWindow.getBtnBigStepPreviousFrame()) {
			drawController.rewindFrames(-BIG_STEP);
			updateControls();
		}
		if (e.getSource() == mainWindow.getMntmZoomIn() || e.getSource() == mainWindow.getBtnZoomIn()) {
			drawController.zoomIn();
			updateControls();
		}
		if (e.getSource() == mainWindow.getMntmZoomOut() || e.getSource() == mainWindow.getBtnZoomOut()) {
			drawController.zoomOut();
			updateControls();
		}
		if (e.getSource() == mainWindow.getMntmInfo() || e.getSource() == mainWindow.getBtnInfo()) {
			showInfoDialog();
		}
		if (e.getSource() == mainWindow.getChckbxmntmShowCoverageOnly()) {
			drawController.setCoverageOnlyMode(mainWindow.getChckbxmntmShowCoverageOnly().isSelected());
		}
		if (e.getSource() == mainWindow.getBtnPlaystop()) {
			if (!playTimer.isRunning()) {
				playTimer.start();
			} else {
				playTimer.stop();
			}
			updateControls();
		}
		if (e.getSource().equals(playTimer)) {
			drawController.rewindFrames(TIMER_STEP);
			if (drawController.getCurrentFrame() + TIMER_STEP >= drawController.getFrameCount()) {
				playTimer.stop();
			}
			updateControls();
		}
	}

	private void exit() {
		System.exit(0);
	}

	/*
	 * (non-Javadoc)
	 * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
	 */
	@Override
	public void mouseDragged(MouseEvent e) {
		// left empty on purpose
	}

	/*
	 * (non-Javadoc)
	 * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
	 */
	@Override
	public void mouseMoved(MouseEvent e) {
		if (mainWindow.getInfoFrame().isVisible()) {
			boolean hovering = false;
			Map<Shape, Balloon> shapeMapping = mainWindow.getPnlCanvas().getShapeMapping();
			for (Map.Entry<Shape, Balloon> entry : shapeMapping.entrySet()) {
				if (entry.getKey().contains(e.getPoint())) {
					mainWindow.getInfoFrame().updateWindow(entry.getValue());
					hovering = true;
					break;
				}
			}

			// If mouse is not hovering
			if (!hovering) {
				mainWindow.getInfoFrame().clearWindow();
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see java.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event.MouseWheelEvent)
	 */
	@Override
	public void mouseWheelMoved(MouseWheelEvent e) {
		int clicks = e.getWheelRotation();
		if (clicks > 0) {
			mainWindow.getSlider().setValue(mainWindow.getSlider().getValue() + BIG_STEP);
		}
		if (clicks < 0) {
			mainWindow.getSlider().setValue(mainWindow.getSlider().getValue() - BIG_STEP);
		}
		updateControls();
	}

	private boolean openSimulationResult() {
		JFileChooser fileChooser = new JFileChooser();
		fileChooser.setFileFilter(new FileNameExtensionFilter("Google Loon Simulation result", "json", "result"));
		fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

		int retVal = fileChooser.showOpenDialog(mainWindow.getFrmMain());
		if (retVal == JFileChooser.APPROVE_OPTION) {
			File selectedFile = fileChooser.getSelectedFile();
			if (log.isInfoEnabled()) {
				log.info("Opening file: " + selectedFile.getAbsolutePath());
			}

			try {
				SimulationStateHistory importedHistory = GsonUtil.buildGson().fromJson(new FileReader(selectedFile),
				        SimulationStateHistory.class);
				importedHistory.sort();

				drawController = new DrawController(mainWindow.getScrollableCanvas(), mainWindow.getPnlCanvas(), importedHistory);

				if (log.isInfoEnabled()) {
					log.info("Simulation history imported!");
				}

				mainWindow.updateTitle(true, selectedFile.getAbsolutePath());

				return true;
			} catch (JsonSyntaxException e) {
				log.error(e.getMessage(), e);
				UiUtils.showErrorDialog(mainWindow.getFrmMain());
			} catch (JsonIOException e) {
				log.error(e.getMessage(), e);
				UiUtils.showErrorDialog(mainWindow.getFrmMain());
			} catch (FileNotFoundException e) {
				log.error(e.getMessage(), e);
				UiUtils.showErrorDialog(mainWindow.getFrmMain());
			}
		}

		return false;
	}

	private void showInfoDialog() {
		mainWindow.getInfoFrame().setVisible(true);
	}

	/*
	 * (non-Javadoc)
	 * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
	 */
	@Override
	public void stateChanged(ChangeEvent e) {
		if (e.getSource() == mainWindow.getSlider()) {
			drawController.jumpToFrame(mainWindow.getSlider().getValue());
			updateControls();
		}
		if (e.getSource() == mainWindow.getScrollableCanvas().getViewport()) {
			mainWindow.getPnlCanvas().repaint();
		}
	}

	private void updateControls() {
		mainWindow.getMnSimulation().setEnabled(true);

		/*
		 * Buttons
		 */
		mainWindow.getBtnBigStepNextFrame().setEnabled(drawController.getCurrentFrame() < drawController.getFrameCount() - BIG_STEP);
		mainWindow.getBtnBigStepPreviousFrame().setEnabled(drawController.getCurrentFrame() > BIG_STEP - 1);
		mainWindow.getBtnNext().setEnabled(drawController.getCurrentFrame() != drawController.getFrameCount() - SMALL_STEP);
		mainWindow.getBtnPrevious().setEnabled(drawController.getCurrentFrame() > SMALL_STEP - 1);
		mainWindow.getBtnZoomIn().setEnabled(true);
		mainWindow.getBtnZoomOut().setEnabled(true);
		mainWindow.getBtnInfo().setEnabled(true);
		mainWindow.getBtnPlaystop().setEnabled(true);
		mainWindow.getBtnPlaystop().setText(playTimer.isRunning() ? "Stop" : "Start");

		/*
		 * Slider
		 */
		mainWindow.getSlider().setValue(drawController.getCurrentFrame());
		mainWindow.getSlider().setMinimum(0);
		mainWindow.getSlider().setMaximum(drawController.getFrameCount() - 1);
		mainWindow.getSlider().setMajorTickSpacing((drawController.getFrameCount() - 1) / 100);
		mainWindow.getSlider().setEnabled(true);

		updateTime();
	}

	private void updateTime() {
		long seconds = drawController.getCurrentTime();

		long day = (int) TimeUnit.SECONDS.toDays(seconds);
		long hours = TimeUnit.SECONDS.toHours(seconds) - day * 24;
		long minute = TimeUnit.SECONDS.toMinutes(seconds) - TimeUnit.SECONDS.toHours(seconds) * 60;
		long second = TimeUnit.SECONDS.toSeconds(seconds) - TimeUnit.SECONDS.toMinutes(seconds) * 60;

		mainWindow.getTxtFldTime().setText(
		        day + "d " + (hours < 10 ? "0" : "") + hours + ":" + (minute < 10 ? "0" : "") + minute + ":" + (second < 10 ? "0" : "")
		                + second);
	}
}
